﻿using GC_RF.BusinessEntities;
using GC_RF.BusinessEntities.DTOs;
using GC_RF.Domain.Abstractions.Interfaces.Factories;
using GC_RF.Domain.Abstractions.Interfaces.Helpers;
using GC_RF.Domain.Abstractions.Interfaces.Services;
using GC_RF.Domain.Enums;
using GC_RF.Domain.Extensions;
using GC_RF.Site.Abstractions;
using GC_RF.Site.Helpers;
using GC_RF.Site.Models;
using GC_RF.Site.Models.Account;
using GC_RF.Site.Models.Mail;
using GC_RF.Site.Models.Shared;
using GC_RF.Site.Resources;
using GC_RF.Site.Resources.Constants;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Web.Mvc;

namespace GC_RF.Site.Controllers
{
	public class AccountController : BaseController
	{
		#region Variables

		private readonly IUserService _userService;
		private readonly IAuthenticationService _authenticationService;
		private readonly IMailer _mailer;

		#endregion

		#region Constructors

		public AccountController(IServiceFactory serviceFactory, IHelperFactory helperFactory)
			: base(serviceFactory)
		{
			_userService = ServiceFactory.CreateUserService();
			if (_userService == null)
				throw new ArgumentNullException(string.Format("userService"));

			_authenticationService = ServiceFactory.CreateAuthenticationService();
			if (_authenticationService == null)
				throw new ArgumentNullException(string.Format("authenticationService"));

			if (helperFactory == null)
				throw new ArgumentNullException(string.Format("helperFactory"));

			_mailer = helperFactory.CreateMailer(true, AppConfig.SystemEmailAddress, AppConfig.SystemEmailName);
			if (_mailer == null)
				throw new ArgumentNullException(string.Format("mailer"));
		}

		#endregion

		#region Actions

		public ActionResult Panel()
		{
			var model = new UserPanelModel();
			FillUserPanelModel(model);
			return View(model);
		}

		[HttpGet]
		[AllowAnonymous]
		[Route("login")]
		public ActionResult Login()
		{
			if (Request.IsAuthenticated)
				return RedirectToAction<HomeController>(x => x.Index());

			var model = new LoginModel();
			FillLoginModel(model);

			return View(model);
		}

		[HttpPost]
		[Route("login")]
		public ActionResult Login(LoginModel model)
		{
			if (IsValid && model != null)
			{
				var account = new UserData();
				Execute(() => account = _userService.GetUserByName(model.UserName));

				if (account != null)
				{
					var isValidPass = Execute(() => _userService.IsValidPassword(model.Password, account.PasswordSalt, account.Password));

					if (isValidPass)
					{
						_authenticationService.SignIn(new SignInUserInfo { Name = account.Name, RememberMe = model.RememberMe });
						return RedirectToAction<HomeController>(x => x.Index());
					}

					ModelState.AddModelError("PassNotValid", string.Format(CultureInfo.InvariantCulture, "Неверный логин и/или пароль"));
				}
				else
					ModelState.AddModelError("UserNotFound", string.Format(CultureInfo.InvariantCulture, "Пользователь {0} не найден", model.UserName));
			}
			return View(model);
		}

		[Authorize]
		[Route("logoff")]
		public ActionResult Logoff()
		{
			_authenticationService.SignOut();
			AppContext.CurrentUser = null;
			return RedirectToAction<HomeController>(o => o.Index());
		}

		[HttpGet]
		[AllowAnonymous]
		[Route("registration")]
		public ActionResult Register()
		{
			var model = new RegisterModel();
			return View(model);
		}

		[HttpPost]
		[Route("registration")]
		public ActionResult Register(RegisterModel model)
		{
			if (IsValid && model != null)
			{
				var isExists = Execute(() => _userService.IsUserNameExists(model.UserName));
				if (!isExists)
				{
					isExists = Execute(() => _userService.IsEmailExists(model.Email));
					if (!isExists)
					{
						var user = Execute(() => _userService.GetNewUser(model.UserName, model.Email, model.Password));
						if (IsValid && user != null)
						{
							Execute(() => _userService.Create(user));
							if (IsValid)
								return RedirectToAction<AccountController>(a => a.Welcome());
						}
					}
					else
						ModelState.AddModelError("UserEmailExists", "Пользователь с данным email уже зарегистрирован");
				}
				else
					ModelState.AddModelError("UserNameExists", "Пользователь с таким именем уже зарегистрирован");
			}

			return View(model);
		}

		[Route("welcome")]
		public ActionResult Welcome()
		{
			return View();
		}

		[Authorize]
		[Route("user-settings")]
		public ActionResult Settings()
		{
			var model = new ServicesModel();
			FillSettingsModel(model);
			return View(model);
		}

		[HttpGet]
		[Authorize]
		[Route("change-password")]
		public ActionResult ChangePassword()
		{
			return View(new ChangePasswordModel());
		}

		[HttpPost]
		[Authorize]
		[Route("change-password")]
		public ActionResult ChangePassword(ChangePasswordModel model)
		{
			if (IsValid && AppContext.CurrentUser != null)
			{
				if (Execute(() => _userService.ChangePassword(AppContext.CurrentUser.Id, model.CurrentPassword, model.NewPassword)))
				{
					var successModel = new InformationMessageModel();
					FillSuccessChangePassModel(successModel);
					return View(Views.InformationMessage, successModel);
				}

				ModelState.AddModelError("IncorrectPass", "Пароль не был изменен. Необходимо ввести корректные данные");
			}
			return View(model);
		}

		[HttpGet]
		[AllowAnonymous]
		[Route("forgot-password")]
		public ActionResult ForgotPassword()
		{
			return View(new ForgotPasswordModel());
		}

		[HttpPost]
		[AllowAnonymous]
		[Route("forgot-password")]
		public ActionResult ForgotPassword(ForgotPasswordModel model)
		{
			if (ModelState.IsValid)
			{
				var resetModel = new MailResetPasswordModel
				{
					Token = Execute(() => _userService.GetResetPasswordToken(model.Email, AppConfig.PasswordResetTokenLifetimeMinutes)),
					Email = model.Email,
				};
				if (string.IsNullOrEmpty(resetModel.Token))
					ModelState.AddModelError("ResetPass", "Функция восстановления пароля не доступна в данный момент для данного email адресса");
				else
				{
					_mailer.SendMessageAsync(Views.ResetPasswordMailTemplatePath, resetModel, model.Email);

					var successModel = new InformationMessageModel();
					FillSuccessForgotPassModel(successModel);
					return View(Views.InformationMessage, successModel);
				}
			}
			return View(new ForgotPasswordModel());
		}

		[HttpGet]
		[AllowAnonymous]
		[Route("reset-password/{token?}")]
		public ActionResult ResetPassword(string token)
		{
			if (!Execute(() => _userService.VerifyResetPasswordToken(token)))
				return RedirectToAction<AccountController>(o => o.ForgotPassword());
			return View(new ResetPasswordModel { Token = token });
		}

		[HttpPost]
		[AllowAnonymous]
		[Route("reset-password/{token?}")]
		public ActionResult ResetPassword(ResetPasswordModel model)
		{
			if (IsValid)
			{
				if (!Execute(() => _userService.VerifyResetPasswordToken(model.Token)))
					return RedirectToAction<AccountController>(o => o.ForgotPassword());

				if (Execute(() => _userService.ResetPassword(model.Token, model.Password)))
				{
					var successModel = new InformationMessageModel();
					FillSuccessResetPassModel(successModel);
					return View(Views.InformationMessage, successModel);
				}
			}
			return View(model);
		}

		#endregion

		#region Internal Implementation

		private void FillLoginModel(LoginModel model)
		{
			if (model == null) return;
			model.RememberMe = true;
		}

		private void FillUserPanelModel(UserPanelModel model)
		{
			if (model == null) return;

			model.IsAuthenticated = Request.IsAuthenticated;

			if (!Request.IsAuthenticated || AppContext.CurrentUser == null) return;

			model.UserName = AppContext.CurrentUser.Name;
			model.UserRole = ((UserRole)AppContext.CurrentUser.RoleId).GetName();
		}

		private void FillSuccessChangePassModel(InformationMessageModel model)
		{
			if (model == null) return;

			model.Title = "Пароль успешно изменен";
			model.Text = new List<string>
			{
				"Операция изменения пароля успешно завершена.",
			};
			model.TextType = MessageType.Info;
			model.ImageUrl = StaticResources.SuccessImageUrl;
		}

		private void FillSuccessForgotPassModel(InformationMessageModel model)
		{
			if (model == null) return;

			model.Title = "Восстановление пароля";
			model.Text = new List<string>
			{
				"Если Вы зарегистрированы на нашем сайте под указаным email адрессом - мы вышлем на него ссылку для восстановления пароля.",
				"Если у вас возникли проблемы с получением этой ссылки, пожалуйста, свяжитесь с администраторами."
			};
			model.TextType = MessageType.Info;
			model.ImageUrl = StaticResources.SuccessImageUrl;
		}

		private void FillSuccessResetPassModel(InformationMessageModel model)
		{
			if (model == null) return;

			model.Title = "Изменение пароля";
			model.Text = new List<string>
			{
				"Операция изменения пароля успешно завершена.",
				"Теперь Вы можете авторизироваться на сайте используя новый пароль."
			};
			model.TextType = MessageType.Info;
			model.ImageUrl = StaticResources.SuccessImageUrl;
		}

		private void FillSettingsModel(ServicesModel model)
		{
			if (model == null) return;

			model.Description = "Настройки пользователя";
			model.Items = new List<ServicesItemModel>
			{
				new ServicesItemModel
				{
					Class = FontAwesomClasses.Exchange,
					Header = "Изменение пароля",
					Description = "Изменение старого пароля аккаунта на новый",
					Url = WebBuilder.BuildActionUrl<AccountController>(m => m.ChangePassword())
				}
			};
		}

		#endregion
	}
}